package com.gnerv.sylvanas.authentication.security.config;

import com.gnerv.sylvanas.authentication.security.autoconfigure.SylvanasSecurityConfigProperties;
import com.gnerv.sylvanas.authentication.security.config.filter.VerificationCodeFilter;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

/**
 * @author Gnerv LiGen
 */
@Configuration
@EnableWebSecurity
public class SylvanasSecurityConfig {

    @Resource
    private SylvanasSecurityConfigProperties sylvanasSecurityConfigProperties;

    @Resource
    private AccessDeniedHandler accessDeniedHandler;

    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Resource
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    @Resource
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;

    @Resource
    private SylvanasAuthorizationManager sylvanasAuthorizationManager;

    @Resource
    private VerificationCodeFilter verificationCodeFilter;

    @Bean
    @Order(1)
    public SecurityFilterChain sylvanasSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                // 关闭 cors
                .cors(AbstractHttpConfigurer::disable)
                // 关闭 csrf
                .csrf(AbstractHttpConfigurer::disable)
                // 关闭匿名访问
                .anonymous(AbstractHttpConfigurer::disable)
                // 加入验证码校验
                .addFilterBefore(verificationCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .authorizeHttpRequests(authorize -> authorize
                        // 公开地址配置直接通过
                        .requestMatchers(sylvanasSecurityConfigProperties.getPublicUrl()).permitAll()
                        // 除公开地址外的地址全部需要验证
                        .anyRequest().access(sylvanasAuthorizationManager)
                )
                .exceptionHandling(exceptionHandlingCustomizer -> exceptionHandlingCustomizer
                        // 自定义认证失败处理
                        .authenticationEntryPoint(authenticationEntryPoint)
                        // 自定义授权失败处理
                        .accessDeniedHandler(accessDeniedHandler))
                .formLogin(formLogin -> formLogin
                        // 自定义登录页面
                        .loginPage(sylvanasSecurityConfigProperties.getLoginPageUrl()).permitAll()
                        // 自定义登录地址
                        .loginProcessingUrl(sylvanasSecurityConfigProperties.getLoginUrl()).permitAll()
                        // 自定义登录成功处理
                        .successHandler(authenticationSuccessHandler)
                        // 自定义登录失败处理
                        .failureHandler(authenticationFailureHandler)
                )
                .logout(logout -> logout
                        // 自定义注销地址
                        .logoutUrl(sylvanasSecurityConfigProperties.getLogoutUrl()).permitAll()
                        // 自定义注销成功处理
                        .logoutSuccessHandler(logoutSuccessHandler)
                        .deleteCookies("JSESSIONID")
                )
                .sessionManagement(session -> session
                        // 自定义同一账户同时登录数量
                        .maximumSessions(sylvanasSecurityConfigProperties.getMaximumSessions())
                );

        return http.build();
    }

}
